from datetime import datetime
from decimal import Decimal
from random import randint
from rest_framework.serializers import ModelSerializer
from rest_framework import serializers
from django.db import transaction
from .models import *
from user.serializers import AddressSerializer


class ClassificationSerializer(ModelSerializer):
    class Meta:
        model = Classification
        fields = ['id', 'name', 'parent']

        extra_kwargs = {
            'parent': {'write_only': True}
        }


class ParentClassificationSerializer(ModelSerializer):
    classification_set = ClassificationSerializer(many=True, read_only=True)

    class Meta:
        model = Classification
        fields = ['id', 'name', 'parent', 'classification_set']

        extra_kwargs = {
            'parent': {'write_only': True}
        }


class CommodityImgSerializer(ModelSerializer):
    class Meta:
        model = CommodityImg
        fields = ['src']


class CommoditySerializer(ModelSerializer):
    classification1_name = serializers.CharField(source='classification1.name', read_only=True)
    classification2_name = serializers.CharField(source='classification2.name', read_only=True)
    commodityimg_set = CommodityImgSerializer(many=True, read_only=True)

    class Meta:
        model = Commodity
        exclude = ['is_delete']


class ShoppingCartSerializer(ModelSerializer):
    commodityDetail = serializers.SerializerMethodField()

    class Meta:
        model = ShoppingCart
        fields = '__all__'

    def get_commodityDetail(self, shopping_cart):
        return CommoditySerializer(shopping_cart.commodity).data

    def validate(self, attrs):
        if attrs['number'] <= 0 or attrs['number'] > attrs['commodity'].stock:
            raise serializers.ValidationError('number error')
        return attrs


class OrderSerializer(ModelSerializer):
    cart = serializers.ListField(write_only=True)
    addressDetail = serializers.SerializerMethodField()

    class Meta:
        model = Order
        fields = '__all__'
        read_only_fields = ['order_id', 'total_count', 'total_amount', 'status', 'user']

    def create(self, validated_data):
        """
        根据所需字段，设置所需字段，获取所需字段
        1.生成订单编号
        2.创建订单信息，事务控制（保证订单数量在有效范围内）
            2.1 设置保存点
            2.2 创建订单信息
            2.3 获取购物车中被勾选的商品
                2.3.1 判断是否超出库存，超出回滚
                2.3.2 库存操作：商品库存减少，销量增加
                2.3.3 将购物车中要结算的商品保存到订单商品表中
                2.3.4 得到总数量和总价格
        3.修改订单状态
        4.清除购物车已结算商品
        :param validated_data:校验后的数据
        :return:
        """
        # 1.生成订单编号
        '''
        订单：唯一性，时间+当前结算的用户id+随机数
        '''
        user = self.context['request'].user
        order_id = datetime.now().strftime('%Y%m%d%H%M%S') + '%06d%02d' % (user.id, randint(1, 100))
        pay_method = validated_data.get('pay_method')
        # 2.创建订单信息，事务控制（保证订单数量在有效范围内）
        with transaction.atomic():
            # 2.1 设置保存点
            save_point = transaction.savepoint()
            # 2.2 创建订单信息
            order = Order.objects.create(
                order_id=order_id,
                total_count=0,  # 设置初始值,后面再修改
                total_amount=Decimal('0.00'),
                status=1 if pay_method == 1 else 2,
                pay_method=pay_method,
                address=validated_data.get('address'),
                user=user
            )
            # 2.3 获取购物车中被勾选的商品
            try:
                carts = validated_data.get('cart')
                for cart_id in carts:
                    while True:
                        cart = ShoppingCart.objects.get(id=cart_id)
                        commodity = cart.commodity
                        # 2.3.1 判断是否超出库存，超出回滚
                        # cart.number = 1000  # 伪造超出库存的情况
                        # 目的，乐观锁修改时做检查用的
                        origin_stock = commodity.stock
                        origin_sales = commodity.sales
                        # if cart.number > commodity.stock:
                        if cart.number > origin_stock:
                            raise serializers.ValidationError('库存不足')
                        # 2.3.2 库存操作：商品库存减少，销量增加
                        # commodity.stock -= cart.number
                        # commodity.sales += cart.number
                        # commodity.save()
                        new_stock = origin_stock - cart.number
                        new_sales = origin_sales + cart.number
                        # 乐观锁，update会返回影响的数据的条数
                        res = Commodity.objects.filter(id=commodity.id, stock=origin_stock).update(stock=new_stock,
                                                                                                   sales=new_sales)
                        if not res:  # 购买不成功
                            continue  # 跳出当前循环，进入下一次循环

                        # 2.3.3 将购物车中要结算的商品保存到订单商品表中
                        OrderGoods.objects.create(
                            number=cart.number,
                            price=commodity.price,
                            order=order,
                            commodity=commodity,
                        )
                        # 2.3.4 得到总数量和总价格
                        order.total_count += cart.number
                        order.total_amount += (cart.number * commodity.price)
                        break  # 循环终止
                # 3.修改订单信息
                order.save()
            except Exception as e:
                transaction.savepoint_rollback(save_point)  # 数据处理有任何异常，都回滚回保存点状态
                raise serializers.ValidationError(e)
            else:
                transaction.savepoint_commit(save_point)  # 没有问题，提交

        # 4.清除购物车已结算商品
        ShoppingCart.objects.filter(id__in=carts).delete()
        return order

    def get_addressDetail(self, order):
        return AddressSerializer(order.address).data
